
/*
 * Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
 
package yxy.game.sdk.huawei;

import org.apache.commons.codec.binary.Base64;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.Asserts;
import org.apache.http.util.EntityUtils;

import com.alibaba.fastjson.JSON;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import javax.net.ssl.SSLContext;

public class GameServiceCallDemo
{
    private static final String RETURN_CODE_SUCCEED = "0";
    private static final int HTTP_RESPONSE_STATUS_CODE_OK = 200;
    
    /**        
     *        
     * @param requestUri  Request URL
     * @param requestParams Request parameters in key-value pair format
     * @param cpAuthKey CP-side signature generation private key
     */    

    public static void callGameService(String requestUri, Map<String,String> requestParams, final String cpAuthKey)
    {
        requestParams.put("cpSign",generateCPSign(requestParams,cpAuthKey));
        
        // Parameters in the response
        Map<String,Object> responseParamPairs = doPost(requestUri,requestParams);

        if(responseParamPairs.isEmpty())
        {
            System.out.println("None response parameter.");
        }
        else
        {
            if(RETURN_CODE_SUCCEED.equalsIgnoreCase(getString("rtnCode",responseParamPairs)))
            {
                System.out.println("rtnCode: " + getString("rtnCode",responseParamPairs));
                System.out.println("ts: " + getString("ts",responseParamPairs));
                System.out.println("rtnSign: " + getString("rtnSign",responseParamPairs));
            }
            else
            {
                System.out.println("rtnCode: " + getString("rtnCode",responseParamPairs));
                System.out.println("errMsg: " + getString("errMsg",responseParamPairs));
            }
        }
    }

    private static String getString(String key,Map<String,Object> responseParamPairs)
    {
        Asserts.notNull(responseParamPairs, "responseParamPairs");
        Object value = responseParamPairs.get(key);
        if (value == null) {
            return null;
        }
        return value.toString();
    }

    /**        
     *  Create an HttpClient instance without SSL authentication. The SSL certificate is currently not added for the address "https://jos-api.cloud.huawei.com/gameservice/api/gbClientApi", and therefore SSL authentication needs to be skipped.
     */
    private static CloseableHttpClient getIgnoeSSLClient() throws Exception {
    	   SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, new TrustStrategy() {
    	      @Override
    	      public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
    	         return true;
    	      }
    	   }).build();
    	   // Create an HttpClient.
    	   CloseableHttpClient client = HttpClients.custom().setSSLContext(sslContext).
    	         setSSLHostnameVerifier(new NoopHostnameVerifier()).build();
    	   return client;
    	}
    
    private static Map<String,Object> doPost(String url, Map<String, String> paramaters)
    {
        HttpPost httpReq = new HttpPost(url);

        // Create an HttpClient instance without SSL authentication.
        CloseableHttpClient httpclient = null ;
		try {
			httpclient = getIgnoeSSLClient();
		} catch (Exception e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
       
        try
        {
            if (paramaters != null)
            {
                List<NameValuePair> paramPairs = new ArrayList<NameValuePair>();

                BasicNameValuePair bnv;

                for (Map.Entry<String, String> entry : paramaters.entrySet())
                {
                    bnv = new BasicNameValuePair(entry.getKey(), entry.getValue());
                    paramPairs.add(bnv);
                }

                httpReq.setEntity(new UrlEncodedFormEntity(paramPairs, "UTF-8"));

            }

            Map<String,Object> responseParamPairs = new HashMap<>();

            HttpResponse resp = httpclient.execute(httpReq);

            if(null != resp && HTTP_RESPONSE_STATUS_CODE_OK == resp.getStatusLine().getStatusCode())
            {
                responseParamPairs = JSON.parseObject(EntityUtils.toString(resp.getEntity()));
            }

            return responseParamPairs;
        }
        catch (Exception e)
        {
            e.printStackTrace();

            return null;
        }
        finally
        {
            try
            {
                httpclient.close();
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }
    }
    
    private static String sign(byte[] data, String privateKey)
    {
        try
        {
            byte[] e = Base64.decodeBase64(privateKey);
            PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(e);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
            Signature signature = Signature.getInstance("SHA256WithRSA");
            signature.initSign(privateK);
            signature.update(data);
            String signs = Base64.encodeBase64String(signature.sign());
            return signs;

        }
        catch (Exception var)
        {
            System.out.println("SignUtil.sign error." + var);
            return "";
        }
    }

    /**        
     * Construct a string of sorted parameters based on the parameter map.
     *        
     * @param params        
     * @return        
     */    
    private static String format(Map<String, String> params)
    {
        StringBuffer base = new StringBuffer();
        Map<String, String> tempMap = new TreeMap<String, String>(params);
        // Obtain the base string for calculating nsp_key.
        try
        {
            for (Map.Entry<String, String> entry : tempMap.entrySet())
            {
                String k = entry.getKey();
                String v = entry.getValue();
                base.append(k).append("=").append(URLEncoder.encode(v, "UTF-8")).append("&");
            }
        }
        catch (UnsupportedEncodingException e)
        {
            System.out.println("Encode parameters failed.");
            e.printStackTrace();
        }
        String body = base.toString().substring(0, base.toString().length() - 1);
        // Escape spaces and asterisks (*).
        body = body.replaceAll("\\+", "%20").replaceAll("\\*", "%2A");
        return body;
    }

    private static String generateCPSign(Map<String,String> requestParams,final String cpAuthKey)
    {
        // Sort the request parameters in the message body based on the ASCII codes of their names and URL encode the parameter values.
        String baseStr = format(requestParams);        
        // Sign the encoded request parameter string using the CP-side signature generation private key.
        String cpSign = sign(baseStr.getBytes(Charset.forName("UTF-8")), cpAuthKey);
        return cpSign;
    }
    
}
